package com.hd123.gwt.types;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;

import com.google.gwt.user.client.rpc.IsSerializable;

public class JsBigDecimal implements Serializable, IsSerializable {
  private static final long serialVersionUID = 300100L;

  
  public static JsBigDecimal ZERO = new JsBigDecimal(0);
  public static JsBigDecimal ONE = new JsBigDecimal(1);
  public static JsBigDecimal MINVALUE = new JsBigDecimal(-99999999999.9999);
  public static JsBigDecimal MAXVALUE = new JsBigDecimal(99999999999.9999);
  private double value = 0;
  private int scale = -1;

  public JsBigDecimal() {
  }

  public JsBigDecimal(double d) {
    this(d, -1);
  }

  public JsBigDecimal(double d, int scale) {
    value = d;
    setScale(scale);
  }

  public JsBigDecimal(String s) {
    this(s, -1);
  }

  public JsBigDecimal(String s, int scale) {
    this(Double.parseDouble(s), scale);
  }

  public double doubleValue() {
    return value;
  }

  public JsBigDecimal abs() {
    return new JsBigDecimal(Math.abs(value));
  }

  public JsBigDecimal add(JsBigDecimal d) {
    int rscale = scale > d.scale ? scale : d.scale;
    return new JsBigDecimal(value + d.doubleValue()).setScale(rscale);
  }

  public JsBigDecimal subtract(JsBigDecimal d) {
    int rscale = scale > d.scale ? scale : d.scale;
    return new JsBigDecimal(value - d.doubleValue()).setScale(rscale);
  }

  public JsBigDecimal multiply(JsBigDecimal d) {
    return new JsBigDecimal(value * d.doubleValue());
  }

  public JsBigDecimal divide(JsBigDecimal d) {
    return new JsBigDecimal(value / d.doubleValue());
  }

  public JsBigDecimal divide(JsBigDecimal d, int scale, JsRoundingMode roundingMode) {
    return new JsBigDecimal(value / d.doubleValue()).setScale(scale, roundingMode);
  }

  public JsBigDecimal setScale(int scale) {
    this.scale = scale;
    if (scale != -1)
      value = roundTo(value, scale);
    return this;
  }

  public JsBigDecimal setScale(int scale, JsRoundingMode roundingMode) {
    return setScale(scale);
  }

  public int compareTo(JsBigDecimal d) {
    if (value > d.doubleValue())
      return 1;
    else if (value < d.doubleValue())
      return -1;
    return 0;
  }

  public boolean equals(Object obj) {
    if (!(obj instanceof JsBigDecimal))
      return false;
    JsBigDecimal x = (JsBigDecimal) obj;
    return value == x.value && scale == x.scale;
  }

  public JsBigDecimal[] divideAndRemainder(JsBigDecimal d) {
    double quotient = downTo(value / d.doubleValue(), 0);
    double remainder = value - quotient * d.doubleValue();
    return new JsBigDecimal[] {
      new JsBigDecimal(quotient), new JsBigDecimal(remainder) };
  }

  /**
   * 以下两个方法客户端实现时要改写:
   * 
   * @see emul/com/google/gwt/emul/com/hd123/gwt/types/JsBigDecimal.java
   */
  public String toString() {
    if (scale == -1) {
      return Double.toString(value);
    } else {
      return new BigDecimal(value).setScale(scale, RoundingMode.HALF_UP).toString();
    }
  }

  private double roundTo(double d, int scale) {
    return new BigDecimal(d).setScale(scale, RoundingMode.HALF_UP).doubleValue();
  }
  
  private double downTo(double d, int scale) {
    return new BigDecimal(d).setScale(scale, RoundingMode.DOWN).doubleValue();
  }

}
